﻿Imports System.IO.Ports
Imports System.Text
Imports System.Threading
Imports FL_8x0.FL_800.Model
Imports GalaSoft.MvvmLight.Messaging

Namespace FL_8x0.Common

    Public Class SerialPortServer
        

        Private _mode As String
        Private _version As String

        Private _serialPort As New SerialPort
        Private _strBuffer As String = ""
        Private _strOperateCommand As String

        Private _groupModel As New GroupModel
        Private _resultModel As New ResultModel

        Private _isWritten As Boolean


        Private _cmdQueue As Queue(Of Cmd)

        Private _isRunning As Boolean

        Private _info As New MiddleInfoModel

        Public Const ENQ = 5
        Public Const ACK = 6
        Public Const NAK = 15

        Public Const STX = 2
        Public Const ETX = 3

        Public Property IsRunning() As Boolean
            Get
                Return _isRunning
            End Get
            Set(ByVal value As Boolean)
                _isRunning = value
            End Set
        End Property

        Public Property GroupMode As GroupModel
            Get
                Return _groupModel
            End Get
            Set(value As GroupModel)
                _groupModel = value
            End Set
        End Property

        Public Property IsWritten As Boolean
            Get
                Return Volatile.Read(_isWritten)
            End Get
            Set(value As Boolean)
                Volatile.Write(_isWritten, value)
            End Set
        End Property

        Public Function StartServer(ByVal strMode As String) As Boolean
            If _serialPort.IsOpen Then
                _serialPort.Close()
                RemoveHandler _serialPort.DataReceived, AddressOf DataReceived
            End If
            Dim modes As String() = strMode.Split(",")
            _mode = modes(2)
            _version = modes(3)
            _groupModel.IsMs = _mode.Contains("MS")

            _serialPort.PortName = modes(0)
            _serialPort.BaudRate = Integer.Parse(modes(1))
            _serialPort.DataBits = 8
            _serialPort.StopBits = 1
            _serialPort.Parity = Parity.None
            _serialPort.Handshake = Handshake.None
            AddHandler _serialPort.DataReceived, AddressOf DataReceived
            _cmdQueue = New Queue(Of Cmd)
            Try
                _serialPort.Open()
            Catch ex As Exception
                MsgBox(ex.Message)
                Return False
            End Try
            _isRunning = True
            Return True
        End Function

        Private Sub ReadPressure()
            If Right(_strBuffer, 1) = Chr(NAK) Then
                _strBuffer = ""
                DequeueAndWrite()
                Exit Sub
            End If
            If _strBuffer.LastIndexOf(Chr(ACK)) = 1 And _strBuffer.LastIndexOf(Chr(ETX)) = _strBuffer.Length - 2 Then
                If Asc(_strBuffer(_strBuffer.Length - 1)) = SystemData.BCC(Mid(_strBuffer, 4, _strBuffer.Length - 4)) Then
                    CutBuffer(4) '读取去除头部 06 06 05 02
                    _info.Gp1 = CutBuffer(Len(_strBuffer) - 2)
                    Messenger.Default.Send(New Message("Middle-Info", _info), "DataReceived")
                    CutBuffer(2)
                End If
                DequeueAndWrite()
            End If
        End Sub

        Private Sub ReadGroup()
            '02 000.0 000.0 000.0 000.0 000.0 000.0 000.0 000.0 +000.0 +000.0 +000.0 -000.0 +000.0 +000.0 +000.0 +000.0 +000.0 +000.0 +000.0 +0.0000E+0 +0.0000E+0Pa      kPa    0000000.00000000000000000000000000000000000000 0000 0000
            If Right(_strBuffer, 1) = Chr(NAK) Then
                _strBuffer = ""
                DequeueAndWrite()
                Exit Sub
            End If

            If _strBuffer.LastIndexOf(Chr(ACK)) = 1 And _strBuffer.LastIndexOf(Chr(ETX)) <> -1 And
                _strBuffer.LastIndexOf(Chr(ETX)) = _strBuffer.Length - 2 Then
                If Asc(_strBuffer(_strBuffer.Length - 1)) = SystemData.BCC(Mid(_strBuffer, 4, _strBuffer.Length - 4)) Then

                    CutBuffer(4) '读取去除头部 06 06 05 02

                    _groupModel.GroupId = CutBuffer(2)

                    '环节时间
                    For i = 0 To 7
                        _groupModel.Times(i) = CutBuffer(5)
                    Next

                    '+NG -NG
                    For i = 0 To 5
                        _groupModel.Limits(i) = CutBuffer(6)
                    Next
                    '压力标准 上下限
                    For i = 6 To 10
                        _groupModel.Limits(i) = CutBuffer(6)
                    Next

                    _groupModel.WValoume = CutBuffer(10)
                    _groupModel.MValoume = CutBuffer(10)
                    _groupModel.LeakUnit = CutBuffer(8)
                    _groupModel.PressureUnit = CutBuffer(6)
                    _groupModel.LeakDigit = CutBuffer(5)
                    _groupModel.TimerDigit = CutBuffer(5)
                    _groupModel.UsbMode = CutBuffer(1)
                    _groupModel.RS232Mode = CutBuffer(1)
                    _groupModel.Cylinder = CutBuffer(1)
                    _groupModel.SV5Mode = CutBuffer(8)
                    _groupModel.Sv6Mode = CutBuffer(8)
                    _groupModel.SV7Mode = CutBuffer(8)
                    _groupModel.SV8Mode = CutBuffer(8)
                    _groupModel.MasterMode = CutBuffer(1)

                    If _mode = SystemData.Model_800 And _version <> SystemData.Version_800_113 Then
                        _groupModel.MiddleJudgment = CutBuffer(1) = "1"
                        _groupModel.LeadPDAC = CutBuffer(5)
                        _groupModel.TestPDAC = CutBuffer(5)
                        If _version = SystemData.Version_800_127 Then
                            _strBuffer = ""
                            _strOperateCommand = ReadGroupExtCmd
                            WriteToMachine(Chr(ENQ) & "?" & Chr(ACK))
                            Exit Sub
                        End If
                        Messenger.Default.Send(New Message("", _groupModel), "Group")
                    Else
                        Messenger.Default.Send(New Message("", _groupModel), "Group")
                    End If
                End If
                _strBuffer = ""
                DequeueAndWrite()
            End If
        End Sub

        Private Sub ReadGroupExt()
            If _strBuffer.Length >= 31 Then
                If Asc(_strBuffer(_strBuffer.Length - 1)) = SystemData.BCC(Mid(_strBuffer, 4, _strBuffer.Length - 4)) Then
                    CutBuffer(4)   '去除头部 06 06 05 02

                    'CutBuffer(2)   '组号 
                    '读时间
                    _groupModel.Times(8) = CutBuffer(5)

                    CutBuffer(20)
                    Messenger.Default.Send(New Message("", _groupModel), "Group")
                End If
                _strBuffer = ""
                DequeueAndWrite()
            End If
        End Sub

        Private Sub ReadWave()
            Dim startIndex = _strBuffer.IndexOf(Chr(STX))
            Dim endIndex = _strBuffer.IndexOf(Chr(ETX))

            If startIndex = -1 Then
                _strBuffer = ""
                DequeueAndWrite()
                Exit Sub
            End If

            If startIndex <> 0 Then
                _strBuffer = Mid(_strBuffer, startIndex + 1)
                startIndex = _strBuffer.IndexOf(Chr(STX))
                endIndex = _strBuffer.IndexOf(Chr(ETX))
            End If

            While (startIndex = 0 And endIndex <> -1)
                Debug.Print(Byte.Parse(SystemData.BCC(Mid(_strBuffer, 1, endIndex + 1))))
                If (Len(_strBuffer) < endIndex + 2) Then
                    Exit While
                End If
                If Asc(_strBuffer(endIndex + 1)) = SystemData.BCC(Mid(_strBuffer, 1, endIndex + 1)) Then
                    Call CutBuffer(1)         '02
                    Dim tmp As String         '环节标志
                    tmp = CutBuffer(1)
                    If tmp = "P" Then
                        Messenger.Default.Send(New Message("Process", CInt(CutBuffer(endIndex - 2))), "DataReceived")
                        CutBuffer(2)
                        startIndex = _strBuffer.IndexOf(Chr(STX))
                        endIndex = _strBuffer.IndexOf(Chr(ETX))
                        Continue While
                    ElseIf tmp = "G" Then
                        _info.Gp2 = CutBuffer(endIndex - 2)
                    ElseIf tmp = "T" Then
                        _info.Temp = CutBuffer(endIndex - 2)
                    ElseIf tmp = "D" Then
                        _info.Dp = CutBuffer(endIndex - 2)
                    Else
                        If SystemData.Machine = Model_800 Then
                            ReadWaveResult(tmp)
                        Else
                            ReadMSWaveResult(tmp)
                        End If
                        CutBuffer(2)
                        Exit While
                    End If
                    Messenger.Default.Send(New Message("Middle-Info", _info), "DataReceived")
                    CutBuffer(2)
                    startIndex = _strBuffer.IndexOf(Chr(STX))
                    endIndex = _strBuffer.IndexOf(Chr(ETX))
                Else
                    _strBuffer = ""
                    Exit While
                End If
            End While
        End Sub

        Private Sub WriteGroup()
            If Len(_strBuffer) >= 3 AndAlso Mid(_strBuffer, 1, 3) = Encoding.ASCII.GetString({ACK, ACK, ENQ}) Then
                _strBuffer = ""
                Dim cmd = g_udt800.BuildGroupCmd()
                ' 绕过队列继续写
                WriteToMachine(cmd)
                Exit Sub
            End If
            Debug.Print(_strBuffer)
            If Len(_strBuffer) >= 1 Then
                If Asc(_strBuffer(0)) = ACK Then
                    _strBuffer = ""
                    If (SystemData.Machine = Model_800 And SystemData.Version = Version_800_113) Or SystemData.Machine = Model_800MS Then
                        IsWritten = True
                        DequeueAndWrite()
                    Else
                        ' 正常写
                        _strOperateCommand = WriteGroupExtCmd
                        WriteToMachine(Chr(ENQ) & "(")
                    End If
                Else
                    _strBuffer = ""
                    IsWritten = False
                    DequeueAndWrite()
                End If
            End If
        End Sub

        Private Sub DequeueAndWrite()
            _strOperateCommand = ""
            If _cmdQueue.Count > 0 Then
                Dim cmd As Cmd = _cmdQueue.Dequeue()
                _strOperateCommand = cmd.Type
                WriteToMachine(cmd.Cmd)
            End If
        End Sub

        Private Sub WriteGroupExt()

            Dim startIndex = _strBuffer.IndexOf(Chr(STX))
            Dim endIndex = _strBuffer.IndexOf(Chr(ETX))

            If startIndex = 0 And endIndex <> -1 Then
                CutBuffer(endIndex + 2)
            End If

            If Len(_strBuffer) >= 3 AndAlso Right(_strBuffer, 3) = Encoding.ASCII.GetString({ACK, ACK, ENQ}) Then
                Dim cmd = Chr(STX) & Format(UShort.Parse(g_udt800.GroupId), "00") &
                    FillInSpaces(Format(Double.Parse(g_udt800.Times(8)), g_udt800.TimerDigit), 5) &
                    FillInSpaces("", 20) & Chr(ETX)
                Dim parity = BCC(cmd)
                cmd &= Chr(parity)
                _strBuffer = ""
                WriteToMachine(Chr(ACK) & cmd)
                Exit Sub
            End If
            Debug.Print(String.Join(" ", Encoding.ASCII.GetBytes(_strBuffer)))
            If Len(_strBuffer) >= 1 Then
                If Asc(_strBuffer(0)) = ACK Then
                    _strBuffer = ""
                    IsWritten = True
                Else
                    _strBuffer = ""
                    IsWritten = False
                End If
                DequeueAndWrite()
            End If
        End Sub

        Private Sub ReadHistoryResult()
            Dim startIndex = _strBuffer.IndexOf(Chr(STX))
            Dim endIndex = _strBuffer.IndexOf(Chr(ETX))

            If startIndex = -1 Then
                _strBuffer = ""
                DequeueAndWrite()
                Exit Sub
            End If

            If startIndex <> 0 Then
                _strBuffer = Mid(_strBuffer, startIndex + 1)
                startIndex = _strBuffer.IndexOf(Chr(STX))
                endIndex = _strBuffer.IndexOf(Chr(ETX))
            End If

            While (startIndex = 0 And endIndex <> -1)
                Debug.Print(Byte.Parse(SystemData.BCC(Mid(_strBuffer, 1, endIndex + 1))))
                If (Len(_strBuffer) < endIndex + 2) Then
                    Exit While
                End If
                If Asc(_strBuffer(endIndex + 1)) = SystemData.BCC(Mid(_strBuffer, 1, endIndex + 1)) Then
                    Call CutBuffer(1)         '02
                    Dim tmp As String         '环节标志
                    tmp = CutBuffer(1)
                    If SystemData.Machine = Model_800 Then
                        ReadWaveResult(tmp)
                    Else
                        ReadMSWaveResult(tmp)
                    End If
                    CutBuffer(2)
                    Messenger.Default.Send(New Message("Middle-Info", _info), "DataReceived")
                    startIndex = _strBuffer.IndexOf(Chr(STX))
                    endIndex = _strBuffer.IndexOf(Chr(ETX))
                Else
                    _strBuffer = ""
                    Exit While
                End If
            End While
        End Sub

        Private Sub ReadWaveResult(ByVal no As String)
            _resultModel.No = no & CutBuffer(3)
            _resultModel.Group = CutBuffer(2)
            _resultModel.DateTime = Convert.ToDateTime(CutBuffer(19))
            For i = 0 To 6
                _resultModel.Timers(i) = CutBuffer(5)
            Next
            _resultModel.Sdet = CutBuffer(6)
            _resultModel.Unit = CutBuffer(8)
            _resultModel.Judgment = CutBuffer(4)
            Messenger.Default.Send(New Message("Result", _resultModel), "DataReceived")
        End Sub

        Private Sub ReadMSWaveResult(ByVal no As String)
            _resultModel.No = no & CutBuffer(3)
            _resultModel.Group = CutBuffer(2)
            _resultModel.DateTime = Convert.ToDateTime(CutBuffer(19))
            For i = 0 To 6
                _resultModel.Timers(i) = CutBuffer(5)
            Next
            _resultModel.Sdet = CutBuffer(6)
            _resultModel.Ldet = CutBuffer(6)
            _resultModel.Unit = CutBuffer(8)
            _resultModel.Judgment = CutBuffer(4)
            Messenger.Default.Send(New Message("Result", _resultModel), "DataReceived")
        End Sub

        Private Sub DataReceived(ByVal s As Object, ByVal e As SerialDataReceivedEventArgs)
            Dim serialPort = TryCast(s, SerialPort)
            If serialPort Is Nothing Then
                Exit Sub
            End If

            Dim intBytesToRead = serialPort.BytesToRead

            Dim bytes(intBytesToRead - 1) As Byte

            serialPort.Read(bytes, 0, intBytesToRead)

            _strBuffer &= Encoding.ASCII.GetString(bytes)

            '_strBuffer &= Chr(serialPort.ReadByte())

            'Debug.Print(String.Join(" ", Encoding.ASCII.GetBytes(_strBuffer)))

            If _strOperateCommand = SystemData.ReadGroupCmd Then
                ReadGroup()
            ElseIf _strOperateCommand = SystemData.ReadGroupExtCmd Then
                ReadGroupExt()
            ElseIf _strOperateCommand = SystemData.OperateCmd Then
                ReadOperateResult()
            ElseIf _strOperateCommand = SystemData.WriteGroupCmd Then
                WriteGroup()
            ElseIf _strOperateCommand = SystemData.WriteGroupExtCmd Then
                WriteGroupExt()
            ElseIf _strOperateCommand = SystemData.ReadPressureCmd Then
                ReadPressure()
            ElseIf _strOperateCommand = SystemData.ReadResultCmd Then
                ReadHistoryResult()
            ElseIf _strOperateCommand = SystemData.ReadWaveCmd Then
                ReadWave()
            Else
                ReadWave()
                '_strBuffer = ""
            End If

        End Sub

        Private Sub ReadOperateResult()
            If Len(_strBuffer) > 4 Then
                _strBuffer = ""
            ElseIf Len(_strBuffer) = 4 Then
                _strBuffer = ""
            ElseIf Len(_strBuffer) > 2 Then
                CutBuffer(2)
            End If
            DequeueAndWrite()
        End Sub

        Public Sub Write(ByVal strCommand As String, ByVal strType As String)
            If _cmdQueue IsNot Nothing Then
                _cmdQueue.Enqueue(New Cmd() With{.Type = strType,.Cmd = strCommand})
                If _strOperateCommand = "" Then
                    Dim cmd As Cmd = _cmdQueue.Dequeue
                    _strOperateCommand = cmd.Type
                    WriteToMachine(cmd.Cmd)
                End If
            End If
        End Sub

        Private Sub WriteToMachine(ByVal command As String)
            If Not _serialPort.IsOpen Then
                MsgBox("串口未打开")
                Exit Sub
            End If
            _serialPort.Write(command)
        End Sub

        '======================================================================================
        '模 块 名：CutBuffer
        '入    口：intLength = 指定个数
        '出    口：指定个数的字符
        '引用模块：strUartBuffer
        '功    能：从缓冲区截取指定个数的字符
        '======================================================================================
        Private Function CutBuffer(ByVal intLength As Integer) As String
            If intLength < Len(_strBuffer) Then
                CutBuffer = Mid(_strBuffer, 1, intLength)
                _strBuffer = Mid(_strBuffer, 1 + intLength)
            Else
                CutBuffer = _strBuffer
                _strBuffer = ""
            End If

        End Function

        Public Function StopServer()
            _strBuffer = ""
            If _serialPort.IsOpen Then
                Try
                    RemoveHandler _serialPort.DataReceived, AddressOf DataReceived
                    _serialPort.Close()
                Catch ex As Exception
                    Return False
                End Try
            End If
            Return True
        End Function
        
    End Class
    
    Public  Class Cmd
        Property Type() As String
        Property Cmd() As String
    End Class
    End Namespace